import { defineComponent, ref, reactive, onMounted, PropType, normalizeClass, onBeforeUnmount } from 'vue'
import { useRafFn } from '@vueuse/core'
import { throttle } from '../_util/_'
import ensureRange from '../_util/ensureRange'

export default defineComponent({
  inheritAttrs: false,
  props: {
    initial: Number,
    max: { type: Number, default: Infinity },
    onScroll: Function as PropType<(opt: { y: number }) => void>
  },
  setup(props, { slots, attrs, expose }) {
    const el = ref<HTMLElement>()

    const state = reactive({ startI: 0, endI: 1 })

    const onScroll = throttle(props.onScroll, 200)
    onBeforeUnmount(() => onScroll.cancel())

    let itemH = 0
    let warpH = 0
    onMounted(() => {
      const firstChild = el.value.firstElementChild as HTMLElement
      const style = window.getComputedStyle(firstChild)
      itemH = firstChild.offsetHeight + parseInt(style.marginTop) + parseInt(style.marginBottom)
      warpH = el.value.offsetHeight

      state.endI = Math.ceil(warpH / itemH)
    })

    let y = 0
    function onWheel(e: WheelEvent) {
      if (props.initial != null && y == -props.initial) return
      const deltaY = ensureRange(e.deltaY, -50, 50)
      y = props.initial == null ? y + deltaY : ensureRange(y + deltaY, -props.initial, props.max - props.initial)
      e.stopPropagation()
      e.preventDefault()
      doScroll(y)
    }

    function doScroll(top: number) {
      y = top
      Object.assign(state, calc(top))
      el.value.style.transform = `translateY(${-top + state.startI * itemH}px)`
      onScroll({ y: top })
    }

    function calc(y: number) {
      return {
        startI: Math.floor(y / itemH),
        endI: Math.ceil((y + warpH) / itemH)
      }
    }

    // ================================================== scroll ==================================================

    let st = 0,
      sy = 0,
      ey = 0,
      d = 200
    const { resume, pause } = useRafFn(() => {
      const rate = Math.min((Date.now() - st) / d, 1)
      doScroll(sy + (ey - sy) * rate)
      if (rate == 1) return pause()
    })

    async function scroll(top: number, t = 200) {
      pause()
      sy = y
      ey = top
      st = Date.now()
      d = t
      resume()
    }

    function scrollIndex(i: number, t?: number) {
      scroll(i * itemH, t)
    }

    expose({
      calc,
      scroll,
      scrollIndex
    })

    // =============================================== render =====================================================

    // prettier-ignore
    const genClass = () => normalizeClass(attrs.class).split(' ').map(e => `${e}-scroll`)

    const genItem = (_, i: number) => slots.default({ index: state.startI + i })

    return () => (
      <div class={genClass()} style='overflow: hidden;' onWheel={onWheel}>
        <div {...attrs} ref={el}>
          {Array.from(Array(state.endI - state.startI), genItem).flat()}
        </div>
      </div>
    )
  }
})
